package registries

import (
	"context"
	"net/http"
	"testing"

	"github.com/stackrox/rox/generated/storage"
	"github.com/stackrox/rox/pkg/registries/types"
	"github.com/stretchr/testify/assert"
)

var (
	NameRepoListEnabled  = "ENABLED"
	NameRepoListDisabled = "DISABLED"
	RegistryType         = "FAKE"
)

func TestCreateRegistryWithoutRepoList(t *testing.T) {
	sourced := true

	onlyWithRepoOpts := FactoryOptions{
		CreatorFuncs: []types.CreatorWrapper{creatorWithRepoList},
	}

	withAndWithoutOpts := FactoryOptions{
		CreatorFuncs:                []types.CreatorWrapper{creatorWithRepoList},
		CreatorFuncsWithoutRepoList: []types.CreatorWrapper{creatorWithoutRepoList},
	}

	testCases := map[string]struct {
		opts               FactoryOptions
		envVal             bool
		autogen            bool
		sourcedIntegration bool
		expectedName       string
	}{
		"repo list enabled for autogenerated regs":                          {withAndWithoutOpts, false, true, !sourced, NameRepoListEnabled},
		"repo list disabled for non-autogenerated regs":                     {withAndWithoutOpts, false, false, !sourced, NameRepoListDisabled},
		"repo list disabled for autogenerated regs if feature disabled":     {withAndWithoutOpts, true, true, !sourced, NameRepoListDisabled},
		"repo list disabled for non-autogenerated regs if feature disabled": {withAndWithoutOpts, true, false, !sourced, NameRepoListDisabled},

		"repo list disabled for sourced autogenerated regs":                          {withAndWithoutOpts, false, true, sourced, NameRepoListDisabled},
		"repo list disabled for sourced non-autogenerated regs":                      {withAndWithoutOpts, false, false, sourced, NameRepoListDisabled},
		"repo list disabled for sourced autogenerated regs if feature disableda":     {withAndWithoutOpts, true, true, sourced, NameRepoListDisabled},
		"repo list disabled for sourced non-autogenerated regs if feature disableda": {withAndWithoutOpts, true, false, sourced, NameRepoListDisabled},

		// Remaining tests ensure repo list is enabled when the factory only knows how to create repo list enabled registries.
		"repo list enabled with default factory for autogenerated regs":                         {onlyWithRepoOpts, false, true, !sourced, NameRepoListEnabled},
		"repo list enabled with default factory for non-autogenerated regs":                     {onlyWithRepoOpts, false, false, !sourced, NameRepoListEnabled},
		"repo list enabled with default factory for autogenerated regs if feature disabled":     {onlyWithRepoOpts, true, true, !sourced, NameRepoListEnabled},
		"repo list enabled with defalut factory for non-autogenerated regs if feature disabled": {onlyWithRepoOpts, true, false, !sourced, NameRepoListEnabled},

		"repo list enabled with default factory for sourced autogenerated regs":                         {onlyWithRepoOpts, false, true, sourced, NameRepoListEnabled},
		"repo list enabled with default factory for sourced non-autogenerated regs":                     {onlyWithRepoOpts, false, false, sourced, NameRepoListEnabled},
		"repo list enabled with default factory for sourced autogenerated regs if feature disabled":     {onlyWithRepoOpts, true, true, sourced, NameRepoListEnabled},
		"repo list enabled with defalut factory for sourced non-autogenerated regs if feature disabled": {onlyWithRepoOpts, true, false, sourced, NameRepoListEnabled},
	}

	for name, tc := range testCases {
		t.Run(name, func(t *testing.T) {
			disableRepoListForAll = tc.envVal
			factory := NewFactory(tc.opts)

			ii := &storage.ImageIntegration{Autogenerated: tc.autogen, Type: RegistryType}
			if tc.sourcedIntegration {
				ii.Source = &storage.ImageIntegration_Source{}
			}
			reg, err := factory.CreateRegistry(ii)
			assert.NoError(t, err)
			assert.Equal(t, reg.Name(), tc.expectedName)
		})
	}
}

func newFakeReg(name string) types.Registry {
	return &FakeReg{
		name: name,
	}
}

type FakeReg struct {
	name string
}

var _ types.Registry = (*FakeReg)(nil)

func (*FakeReg) Config(ctx context.Context) *types.Config                  { return nil }
func (*FakeReg) HTTPClient() *http.Client                                  { return nil }
func (*FakeReg) ListTags(_ context.Context, _ string) ([]string, error)    { return nil, nil }
func (*FakeReg) Match(_ *storage.ImageName) bool                           { return false }
func (*FakeReg) Metadata(_ *storage.Image) (*storage.ImageMetadata, error) { return nil, nil }
func (f *FakeReg) Name() string                                            { return f.name }
func (*FakeReg) Test() error                                               { return nil }

func creatorWithoutRepoList() (string, types.Creator) {
	return RegistryType,
		func(integration *storage.ImageIntegration, _ ...types.CreatorOption) (types.Registry, error) {
			reg := newFakeReg(NameRepoListDisabled)
			return reg, nil
		}
}

func creatorWithRepoList() (string, types.Creator) {
	return RegistryType,
		func(integration *storage.ImageIntegration, _ ...types.CreatorOption) (types.Registry, error) {
			reg := newFakeReg(NameRepoListEnabled)
			return reg, nil
		}
}
